{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "code",
      "source": [
        "import os, getpass\n",
        "os.environ[\"GEMINI_API_KEY\"] = getpass.getpass(\"Enter your Gemini API key: \").strip()\n",
        "\n",
        "import subprocess, tempfile, pathlib, textwrap, time, requests, shlex\n",
        "\n",
        "def choose_llm():\n",
        "    if os.getenv(\"OPENAI_API_KEY\"):\n",
        "        return \"openai\", \"gpt-4o-mini\", \"OPENAI_API_KEY\"\n",
        "    if os.getenv(\"GEMINI_API_KEY\"):\n",
        "        return \"gemini\", \"gemini-2.5-flash\", \"GEMINI_API_KEY\"\n",
        "    if os.getenv(\"ANTHROPIC_API_KEY\"):\n",
        "        return \"anthropic\", \"claude-3-5-haiku-20241022\", \"ANTHROPIC_API_KEY\"\n",
        "    raise RuntimeError(\"Set one API key before running.\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "DxTiWbedmzZ5",
        "outputId": "8288b0fc-603b-4424-f3b2-87e5d471744f"
      },
      "execution_count": 7,
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Enter your Gemini API key: ··········\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "def run(cmd, check=True, env=None):\n",
        "    print(\"▸\", cmd)\n",
        "    p = subprocess.run(cmd, shell=True, text=True, capture_output=True, env=env)\n",
        "    if p.stdout: print(p.stdout)\n",
        "    if p.stderr: print(p.stderr)\n",
        "    if check and p.returncode != 0:\n",
        "        raise RuntimeError(f\"Command failed: {cmd}\")\n",
        "    return p"
      ],
      "metadata": {
        "id": "5hSf0lBlm38h"
      },
      "execution_count": 8,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "def ensure_node_and_cipher():\n",
        "    run(\"sudo apt-get update -y && sudo apt-get install -y nodejs npm\", check=False)\n",
        "    run(\"npm install -g @byterover/cipher\")"
      ],
      "metadata": {
        "id": "Aq5wmiDam7TG"
      },
      "execution_count": 9,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "def write_cipher_yml(workdir, provider, model, key_env):\n",
        "    cfg = \"\"\"\n",
        "llm:\n",
        "  provider: {provider}\n",
        "  model: {model}\n",
        "  apiKey: ${key_env}\n",
        "systemPrompt:\n",
        "  enabled: true\n",
        "  content: |\n",
        "    You are an AI programming assistant with long-term memory of prior decisions.\n",
        "embedding:\n",
        "  disabled: true\n",
        "mcpServers:\n",
        "  filesystem:\n",
        "    type: stdio\n",
        "    command: npx\n",
        "    args: ['-y','@modelcontextprotocol/server-filesystem','.']\n",
        "\"\"\".format(provider=provider, model=model, key_env=key_env)\n",
        "\n",
        "    (workdir / \"memAgent\").mkdir(parents=True, exist_ok=True)\n",
        "    (workdir / \"memAgent\" / \"cipher.yml\").write_text(cfg.strip() + \"\\n\")"
      ],
      "metadata": {
        "id": "kZrizHBIm8Yo"
      },
      "execution_count": 10,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "def cipher_once(text, env=None, cwd=None):\n",
        "    cmd = f'cipher {shlex.quote(text)}'\n",
        "    p = subprocess.run(cmd, shell=True, text=True, capture_output=True, env=env, cwd=cwd)\n",
        "    print(\"Cipher says:\\n\", p.stdout or p.stderr)\n",
        "    return p.stdout.strip() or p.stderr.strip()"
      ],
      "metadata": {
        "id": "qOIIE_jWnA81"
      },
      "execution_count": 11,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "def start_api(env, cwd):\n",
        "    proc = subprocess.Popen(\"cipher --mode api\", shell=True, env=env, cwd=cwd,\n",
        "                            stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)\n",
        "    for _ in range(30):\n",
        "        try:\n",
        "            r = requests.get(\"http://127.0.0.1:3000/health\", timeout=2)\n",
        "            if r.ok:\n",
        "                print(\"API /health:\", r.text)\n",
        "                break\n",
        "        except: pass\n",
        "        time.sleep(1)\n",
        "    return proc"
      ],
      "metadata": {
        "id": "mlQ3WBTCnCin"
      },
      "execution_count": 12,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "def main():\n",
        "    provider, model, key_env = choose_llm()\n",
        "    ensure_node_and_cipher()\n",
        "    workdir = pathlib.Path(tempfile.mkdtemp(prefix=\"cipher_demo_\"))\n",
        "    write_cipher_yml(workdir, provider, model, key_env)\n",
        "    env = os.environ.copy()\n",
        "\n",
        "    cipher_once(\"Store decision: use pydantic for config validation; pytest fixtures for testing.\", env, str(workdir))\n",
        "    cipher_once(\"Remember: follow conventional commits; enforce black + isort in CI.\", env, str(workdir))\n",
        "\n",
        "    cipher_once(\"What did we standardize for config validation and Python formatting?\", env, str(workdir))\n",
        "\n",
        "    api_proc = start_api(env, str(workdir))\n",
        "    time.sleep(3)\n",
        "    api_proc.terminate()\n",
        "\n",
        "if __name__ == \"__main__\":\n",
        "    main()"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "MctR5mT4mVhx",
        "outputId": "de97757f-4ef0-43fd-c4f2-04f5fdad23ea"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "▸ sudo apt-get update -y && sudo apt-get install -y nodejs npm\n",
            "Hit:1 http://archive.ubuntu.com/ubuntu jammy InRelease\n",
            "Hit:2 http://archive.ubuntu.com/ubuntu jammy-updates InRelease\n",
            "Hit:3 https://deb.nodesource.com/node_20.x nodistro InRelease\n",
            "Hit:4 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64  InRelease\n",
            "Hit:5 http://archive.ubuntu.com/ubuntu jammy-backports InRelease\n",
            "Hit:6 https://cloud.r-project.org/bin/linux/ubuntu jammy-cran40/ InRelease\n",
            "Hit:7 http://security.ubuntu.com/ubuntu jammy-security InRelease\n",
            "Hit:8 https://r2u.stat.illinois.edu/ubuntu jammy InRelease\n",
            "Hit:9 https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu jammy InRelease\n",
            "Hit:10 https://ppa.launchpadcontent.net/graphics-drivers/ppa/ubuntu jammy InRelease\n",
            "Hit:11 https://ppa.launchpadcontent.net/ubuntugis/ppa/ubuntu jammy InRelease\n",
            "Reading package lists...\n",
            "Reading package lists...\n",
            "Building dependency tree...\n",
            "Reading state information...\n",
            "Some packages could not be installed. This may mean that you have\n",
            "requested an impossible situation or if you are using the unstable\n",
            "distribution that some required packages have not yet been created\n",
            "or been moved out of Incoming.\n",
            "The following information may help to resolve the situation:\n",
            "\n",
            "The following packages have unmet dependencies:\n",
            " nodejs : Conflicts: npm\n",
            " npm : Depends: node-cacache but it is not going to be installed\n",
            "       Depends: node-gyp but it is not going to be installed\n",
            "       Depends: node-mkdirp but it is not going to be installed\n",
            "       Depends: node-nopt but it is not going to be installed\n",
            "       Depends: node-tar but it is not going to be installed\n",
            "       Depends: node-which but it is not going to be installed\n",
            "       Depends: nodejs:any (>= 10)\n",
            "       Recommends: node-tap but it is not going to be installed\n",
            "\n",
            "W: Skipping acquire of configured file 'main/source/Sources' as repository 'https://r2u.stat.illinois.edu/ubuntu jammy InRelease' does not seem to provide it (sources.list entry misspelt?)\n",
            "E: Unable to correct problems, you have held broken packages.\n",
            "\n",
            "▸ npm install -g @byterover/cipher\n",
            "\n",
            "changed 389 packages in 20s\n",
            "\n",
            "51 packages are looking for funding\n",
            "  run `npm fund` for details\n",
            "\n",
            "npm warn deprecated node-domexception@1.0.0: Use your platform's native DOMException instead\n",
            "npm warn deprecated @azure/openai@1.0.0-beta.13: The Azure OpenAI client library for JavaScript beta has been retired. Please migrate to the stable OpenAI SDK for JavaScript using the migration guide: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/openai/openai/MIGRATION.md.\n",
            "\n",
            "Cipher says:\n",
            " 05:33:23 ERROR: No .env file found at /usr/local/lib/node_modules/@byterover/cipher/dist/.env, copy .env.example to .env and fill in the values\n",
            "\n",
            "Cipher says:\n",
            " 05:33:24 ERROR: No .env file found at /usr/local/lib/node_modules/@byterover/cipher/dist/.env, copy .env.example to .env and fill in the values\n",
            "\n",
            "Cipher says:\n",
            " 05:33:26 ERROR: No .env file found at /usr/local/lib/node_modules/@byterover/cipher/dist/.env, copy .env.example to .env and fill in the values\n",
            "\n"
          ]
        }
      ]
    }
  ]
}